package com.kinghunt.misp.service.impl.userx;

import com.kinghunt.misp.common.dto.userx.OrganizationDTO;
import com.kinghunt.misp.common.dto.userx.RoleDTO;
import com.kinghunt.misp.common.dto.userx.UserDTO;
import com.kinghunt.misp.common.param.userx.UserLoginParam;
import com.kinghunt.misp.common.param.userx.UserQueryCondition;
import com.kinghunt.misp.common.utils.*;
import com.kinghunt.misp.dal.dao.userx.*;
import com.kinghunt.misp.dal.model.userx.*;
import com.kinghunt.misp.service.api.userx.UserReadService;
import com.bygoodtech.kings.base.utils.ConvertUtil;
import com.bygoodtech.kings.common.dto.ApiResult;
import com.bygoodtech.kings.common.dto.page.PageInfo;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Description 用户
 * @Author jiangtao
 * @Date 2023/7/5 22:22
 */
@Service
public class UserReadServiceImpl implements UserReadService {

    private final UserDAO userDao;
    private final OrganizationUserDAO organizationUserDAO;
    private final OrganizationRoleDAO organizationRoleDAO;
    private final UserRoleDAO userRoleDAO;
    private final StringRedisTemplate redisTemplate;
    private final SmsClient smsClient;
    private final OrganizationDAO organizationDAO;
    private final RoleDAO roleDAO;

    public UserReadServiceImpl(UserDAO userDao, OrganizationUserDAO organizationUserDAO,
                               OrganizationRoleDAO organizationRoleDAO,
                               UserRoleDAO userRoleDAO, StringRedisTemplate redisTemplate,
                               SmsClient smsClient, OrganizationDAO organizationDAO,
                               RoleDAO roleDAO) {
        this.userDao = userDao;
        this.organizationUserDAO = organizationUserDAO;
        this.organizationRoleDAO = organizationRoleDAO;
        this.userRoleDAO = userRoleDAO;
        this.redisTemplate = redisTemplate;
        this.smsClient = smsClient;
        this.organizationDAO = organizationDAO;
        this.roleDAO = roleDAO;
    }
    
    @Override
    public ApiResult<UserDTO> getUserById(Long id) {
        List<UserDO> userByIds = userDao.getUserByIds(Collections.singletonList(id));
        if (CollectionUtils.isEmpty(userByIds)) {
            return ApiResult.ok(null);
        }
        UserDTO dto = ConvertUtil.convert(userByIds.get(0), UserDTO.class);

        Long orgId = dto.getOrganizationId();
        if (Objects.isNull(orgId)) {
            return ApiResult.ok(dto);
        }
        OrganizationDO orgDO = organizationDAO.selectByPrimaryKey(orgId);
        OrganizationDTO organizationDTO = ConvertUtil.convert(orgDO, OrganizationDTO.class);
        dto.setOrganization(organizationDTO);

        List<Long> roleIds = userRoleDAO.selectRoleIdsByUserId(id);
        if (CollectionUtils.isEmpty(roleIds)) {
            return ApiResult.ok(dto);
        }
        List<RoleDO> roles = roleDAO.selectByIds(roleIds);
        List<RoleDTO> roleDTOS = ConvertUtil.convert(roles, RoleDTO.class);
        dto.setRoles(roleDTOS);
        if (!CollectionUtils.isEmpty(roleDTOS) && Objects.nonNull(roles.get(0))) {
            dto.setRoleId(roleDTOS.get(0).getId());
            dto.setRoleName(roleDTOS.get(0).getName());
        }

        return ApiResult.ok(dto);
    }

    @Override
    public ApiResult<List<UserDTO>> getUsersByIds(List<Long> ids) {
        List<UserDO> userByIds = userDao.getUserByIds(ids);
        List<UserDTO> dtos = ConvertUtil.convert(userByIds, UserDTO.class);
        return ApiResult.ok(dtos);
    }

    @Override
    public ApiResult<List<UserDTO>> getUsersByOrganizationId(Long orgId) {
        // 机构和用户表，机构和角色&角色和用户表
        List<Long> userIdList = organizationUserDAO.selectUserIdsByOrgId(orgId);
        //List<Long> roleIdList = organizationRoleDAO.selectRoleIdsByOrgId(orgId);
        //List<Long> roleUserIdList = userRoleDAO.selectUserByRoleId(roleIdList);
        //userIdList.addAll(roleUserIdList);
        List<UserDO> userByIds = userDao.getUserByIds(userIdList);
        List<UserDTO> dtos = ConvertUtil.convert(userByIds, UserDTO.class);
        return ApiResult.ok(dtos);
    }

    @Override
    public ApiResult<PageInfo<UserDTO>> searchUsers(UserQueryCondition condition) {
        int offset = (condition.getPageNum() - 1) * condition.getPageSize();
        List<UserDO> userDOList = userDao.pageByUsernameAndOrgId(condition.getUsername(),
                condition.getOrganizationId(), offset, condition.getPageSize());
        int count = userDao.countByUsernameAndOrgId(condition.getUsername(), condition.getOrganizationId());
        PageInfo<UserDTO> pageInfo = new PageInfo<>();
        List<UserDTO> UserDTOs = ConvertUtil.convert(userDOList, UserDTO.class);
        pageInfo.setData(UserDTOs);
        pageInfo.setTotal(count);
        return ApiResult.ok(pageInfo);
    }

    @Override
    public ApiResult<String> loginWithNameAndPassword(String userName, String password) {
        UserDO userByName = userDao.getUserByUsername(userName);
        UserDO userByMobilePhone = userDao.getUserByMobilePhone(userName);
        if (userByName == null && userByMobilePhone == null) {
            return ApiResult.failOfMessage("user not exists");
        }
        UserDO userDO = userByName;
        if (userDO == null) {
            userDO = userByMobilePhone;
        }
        if (PasswordUtils.passwordEncoder(password, userDO.getSalt()).equals(userDO.getPassword())) {
            return ApiResult.ok(TokenUtils.toToken(userDO.getId()));
        }
        return ApiResult.failOfMessage("invalid password or user name");
    }

    @Override
    public ApiResult<String> login(UserLoginParam param) {
        UserDO userDO = userDao.getUserByUsernameAndOrgId(param.getUsername(), param.getOrgId());
        if (Objects.isNull(userDO)) {
            return ApiResult.failOfMessage("user not exists");
        }
        if (PasswordUtils.passwordEncoder(param.getPassword(), userDO.getSalt()).equals(userDO.getPassword())) {
            // return ApiResult.ok(userDO.getId());
            return ApiResult.ok(TokenUtils.toToken(userDO.getId()));
        }
        return ApiResult.failOfMessage("invalid password or user name");
    }

    @Override
    public ApiResult<String> loginWithMobilePhoneAndVerifyCode(String mobilePhone, String verifyCode) {
        String verifyCodeKey = CacheKeyUtils.getVerifyCodeKey(mobilePhone);
        String localVerifyCode = redisTemplate.opsForValue().get(verifyCodeKey);
        if (StringUtils.isEmpty(localVerifyCode) || !verifyCode.equals(localVerifyCode)) {
            return ApiResult.failOfMessage("verifyCode error");
        }
        UserDO userByMobilePhone = userDao.getUserByMobilePhone(mobilePhone);
        if (userByMobilePhone != null) {
            return ApiResult.ok(TokenUtils.toToken(userByMobilePhone.getId()));
        }
        UserDO userDO = new UserDO();
        userDO.setUsername(CommonUtils.generateUserName());
        userDO.setPhone(mobilePhone);
        int result = userDao.addUser(userDO);
        if (result != 1) {
            return ApiResult.failOfMessage("server error, try again");
        }
        return ApiResult.ok(TokenUtils.toToken(userDO.getId()));
    }

    @Override
    public ApiResult<String> getVerifyCodeByPhone(String mobilePhone) {
        String verifyCodeKey = CacheKeyUtils.getVerifyCodeKey(mobilePhone);
        String verifyCode = redisTemplate.opsForValue().get(verifyCodeKey);
        if (!StringUtils.isEmpty(verifyCode)) {
            return ApiResult.ok(verifyCode);
        }
        String generateVerifyCode = CommonUtils.generateVerifyCode();
        smsClient.sendVerifyCode(mobilePhone, generateVerifyCode);
        redisTemplate.opsForValue().set(verifyCodeKey, generateVerifyCode, 90, TimeUnit.SECONDS);
        return ApiResult.ok(generateVerifyCode);
    }

    @Override
    public ApiResult<Boolean> validateUserName(String userName) {
        UserDO userByName = userDao.getUserByUsername(userName);
        if (userByName != null) {
            return ApiResult.ok(false);
        }
        return ApiResult.ok(true);
    }
}
